package com.cfpamf.ms.insur.wx.response;

import com.cfpamf.ms.insur.wx.exception.WxPayException;
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty;
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlRootElement;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.apache.commons.codec.digest.DigestUtils;

import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;
import java.util.Base64;

/**
 * 当商户申请的退款有结果后，微信会把相关结果发送给商户，商户需要接收处理，并返回应答。
 * 对后台通知交互时，如果微信收到商户的应答不是成功或超时，微信认为通知失败，微信会通过一定的策略定期重新发起通知，尽可能提高通知的成功率，但微信不保证通知最终能成功（通知频率为15s/15s/30s/3m/10m/20m/30m/30m/30m/60m/3h/3h/3h/6h/6h - 总计 24h4m）。
 * 注意：同样的通知可能会多次发送给商户系统。商户系统必须能够正确处理重复的通知。
 * 推荐的做法是，当收到通知进行处理时，首先检查对应业务数据的状态，判断该通知是否已经处理过，如果没有处理过再进行处理，如果处理过直接返回结果成功。在对业务数据进行状态检查和处理之前，要采用数据锁进行并发控制，以避免函数重入造成的数据混乱。
 * 特别说明：退款结果对重要的数据进行了加密，商户需要用商户秘钥进行解密后才能获得结果通知的内容
 * 解密方式
 * <p>
 * 解密步骤如下：
 * （1）对加密串A做base64解码，得到加密串B
 * （2）对商户key做md5，得到32位小写key* ( key设置路径：微信商户平台(pay.weixin.qq.com)-->账户设置-->API安全-->密钥设置 )
 * <p>
 * （3）用key*对加密串B做AES-256-ECB解密（PKCS7Padding）
 */
@Data
public class WxPayRefundNotifyResponse extends WxPayResponse {


    /**
     * <pre>
     * 字段名：加密信息.
     * 变量名：req_info
     * 是否必填：是
     * 类型：String(1024)
     * 描述：
     * 解密步骤如下：
     * （1）对加密串A做base64解码，得到加密串B
     * （2）对商户key做md5，得到32位小写key* ( key设置路径：微信商户平台(pay.weixin.qq.com)-->账户设置-->API安全-->密钥设置 )
     *
     * （3）用key*对加密串B做AES-256-ECB解密（PKCS7Padding）
     * </pre>
     */
    @JacksonXmlProperty(localName = "req_info")
    private String reqInfoCiphertext;

    private ReqInfo reqInfo;

    /**
     * 这个地方只要解析成功就是true
     *
     * @return
     */
    @Override
    public boolean isSuccess() {
        return reqInfo != null;
    }

    public String decodeCiphertext(String apiKey) {

        try {
            // * （1）对加密串A做base64解码，得到加密串B
            byte[] decode = Base64.getDecoder().decode(reqInfoCiphertext);
            // * （2）对商户key做md5，得到32位小写key* ( key设置路径：微信商户平台(pay.weixin.qq.com)-->账户设置-->API安全-->密钥设置 )
            String keyMd5String = DigestUtils.md5Hex(apiKey);
            SecretKeySpec key = new SecretKeySpec(keyMd5String.getBytes(StandardCharsets.UTF_8), "AES");
            Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
            cipher.init(Cipher.DECRYPT_MODE, key);
            return new String(cipher.doFinal(decode), StandardCharsets.UTF_8);
        } catch (Exception e) {
            throw new WxPayException("解密退款通知密文失败:" + reqInfoCiphertext, e);
        }
    }


    /**
     * 加密信息字段解密后的内容.
     */
    @Data
    @NoArgsConstructor
    @JacksonXmlRootElement(localName = "root")
    public static class ReqInfo {

        /**
         * <pre>
         * 字段名：微信订单号.
         * 变量名：transaction_id
         * 是否必填：是
         * 类型：String(32)
         * 示例值：1217752501201407033233368018
         * 描述：微信订单号
         * </pre>
         */
        @JacksonXmlProperty(localName = "transaction_id")
        private String transactionId;

        /**
         * <pre>
         * 字段名：商户订单号.
         * 变量名：out_trade_no
         * 是否必填：是
         * 类型：String(32)
         * 示例值：1217752501201407033233368018
         * 描述：商户系统内部的订单号
         * </pre>
         */
        @JacksonXmlProperty(localName = "out_trade_no")
        private String outTradeNo;

        /**
         * <pre>
         * 字段名：微信退款单号.
         * 变量名：refund_id
         * 是否必填：是
         * 类型：String(28)
         * 示例值：1217752501201407033233368018
         * 描述：微信退款单号
         * </pre>
         */
        @JacksonXmlProperty(localName = "refund_id")
        private String refundId;

        /**
         * <pre>
         * 字段名：商户退款单号.
         * 变量名：out_refund_no
         * 是否必填：是
         * 类型：String(64)
         * 示例值：1217752501201407033233368018
         * 描述：商户退款单号
         * </pre>
         */
        @JacksonXmlProperty(localName = "out_refund_no")
        private String outRefundNo;

        /**
         * <pre>
         * 字段名：订单金额.
         * 变量名：total_fee
         * 是否必填：是
         * 类型：Int
         * 示例值：100
         * 描述：订单总金额，单位为分，只能为整数，详见支付金额
         * </pre>
         */
        @JacksonXmlProperty(localName = "total_fee")
        private Integer totalFee;

        /**
         * <pre>
         * 字段名：结订单金额.
         * 变量名：settlement_total_fee
         * 是否必填：否
         * 类型：Int
         * 示例值：100
         * 描述：当该订单有使用非充值券时，返回此字段。应结订单金额=订单金额-非充值代金券金额，应结订单金额<=订单金额。
         * </pre>
         */
        @JacksonXmlProperty(localName = "settlement_total_fee")
        private Integer settlementTotalFee;

        /**
         * <pre>
         * 字段名：申请退款金额.
         * 变量名：refund_fee
         * 是否必填：是
         * 类型：Int
         * 示例值：100
         * 描述：退款总金额,单位为分
         * </pre>
         */
        @JacksonXmlProperty(localName = "refund_fee")
        private Integer refundFee;

        /**
         * <pre>
         * 字段名：退款金额.
         * 变量名：settlement_refund_fee
         * 是否必填：是
         * 类型：Int
         * 示例值：100
         * 描述：退款金额=申请退款金额-非充值代金券退款金额，退款金额<=申请退款金额
         * </pre>
         */
        @JacksonXmlProperty(localName = "settlement_refund_fee")
        private Integer settlementRefundFee;

        /**
         * <pre>
         * 字段名：退款状态.
         * 变量名：refund_status
         * 是否必填：是
         * 类型：String(16)
         * 示例值：SUCCESS
         * 描述：SUCCESS-退款成功，CHANGE-退款异常，REFUNDCLOSE—退款关闭
         * </pre>
         */
        @JacksonXmlProperty(localName = "refund_status")
        private String refundStatus;

        /**
         * <pre>
         * 字段名：退款成功时间.
         * 变量名：success_time
         * 是否必填：否
         * 类型： String(20)
         * 示例值：2017-12-15 09:46:01
         * 资金退款至用户帐号的时间，格式2017-12-15 09:46:01
         * </pre>
         */
        @JacksonXmlProperty(localName = "success_time")
        private String successTime;

        /**
         * <pre>
         * 字段名：退款入账账户.
         * 变量名：refund_recv_accout
         * 是否必填：是
         * 类型：String(64)
         * 示例值：招商银行信用卡0403
         * 描述：取当前退款单的退款入账方，1）退回银行卡：{银行名称}{卡类型}{卡尾号}，2）退回支付用户零钱:支付用户零钱 ，3）退还商户: 商户基本账户，商户结算银行账户，4）退回支付用户零钱通: 支付用户零钱通
         * </pre>
         */
        @JacksonXmlProperty(localName = "refund_recv_accout")
        private String refundRecvAccout;

        /**
         * <pre>
         * 字段名：退款资金来源.
         * 变量名：refund_account
         * 是否必填：是
         * 类型：String(30)
         * 示例值：REFUND_SOURCE_RECHARGE_FUNDS
         * 描述：REFUND_SOURCE_RECHARGE_FUNDS 可用余额退款/基本账户，REFUND_SOURCE_UNSETTLED_FUNDS 未结算资金退款
         * </pre>
         */
        @JacksonXmlProperty(localName = "refund_account")
        private String refundAccount;

        /**
         * <pre>
         * 字段名：退款发起来源.
         * 变量名：refund_request_source
         * 是否必填：是
         * 类型：String(30)
         * 示例值：API
         * 描述：API接口，VENDOR_PLATFORM商户平台
         * </pre>
         */
        @JacksonXmlProperty(localName = "refund_request_source")
        private String refundRequestSource;

    }
//
//    @JacksonXmlProperty(localName = "appid")
//    @JacksonXmlCData
//    private String appId;
//
//    @JacksonXmlProperty(localName = "mch_id")
//    @JacksonXmlCData
//    private String mchId;
//
//    @JacksonXmlProperty(localName = "nonce_str")
//    @JacksonXmlCData
//    private String nonceStr;
//
//    @JacksonXmlProperty(localName = "sign")
//    @JacksonXmlCData
//    private String sign;
//
//    // 微信订单号
//    @JacksonXmlProperty(localName = "transaction_id")
//    @JacksonXmlCData
//    private String transactionId;
//
//    // 商户订单号， 商户内部系统的订单号，  32个字符内，可包含字母。 微信支付要求商户订单号保持唯一性
//    @JacksonXmlProperty(localName = "out_trade_no")
//    @JacksonXmlCData
//    private String outTradeNo;
//
//    // 微信退款单号
//    @JacksonXmlProperty(localName = "refund_id")
//    @JacksonXmlCData
//    private String refundId;
//
//    // 商户退款单号
//    @JacksonXmlProperty(localName = "out_refund_no")
//    @JacksonXmlCData
//    private String outRefundNo;
//
//    // 订单金额
//    @JacksonXmlProperty(localName = "total_fee")
//    @JacksonXmlCData
//    private String totalFee;
//
//    // 应结订单金额
//    @JacksonXmlProperty(localName = "settlement_total_fee")
//    @JacksonXmlCData
//    private String settlementTotalFee;
//
//    // 申请退款金额
//    @JacksonXmlProperty(localName = "refund_fee")
//    @JacksonXmlCData
//    private String refundFee;
//
//    // 退款金额
//    @JacksonXmlProperty(localName = "settlement_refund_fee")
//    @JacksonXmlCData
//    private String settlementRefundFee;
//
//    /**
//     * 退款状态
//     */
//    @JacksonXmlProperty(localName = "refund_status")
//    @JacksonXmlCData
//    private String refundStatus;
//
//    /**
//     * 退款成功时间
//     */
//    @JacksonXmlProperty(localName = "success_time")
//    @JacksonXmlCData
//    private String successTime;
//
//    /**
//     * 退款入账账户
//     */
//    @JacksonXmlProperty(localName = "refund_recv_accout")
//    @JacksonXmlCData
//    private String refundRecvAccout;
//
//    /**
//     * 退款资金来源
//     */
//    @JacksonXmlProperty(localName = "refund_account")
//    @JacksonXmlCData
//    private String refundAccount;
//
//    /**
//     * 退款发起来源
//     */
//    @JacksonXmlProperty(localName = "refund_request_source")
//    @JacksonXmlCData
//    private String refundRequestSource;
}
